package p45_spring代理的特点;

import org.springframework.aop.framework.Advised;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.lang.reflect.Method;

/**
 * spring代理的几个个特点
 * 1.依赖注入和初始化影响的是原始对象，这两个阶段并不会被增强(因为无循环依赖时，创建代理的时机是后初始化阶段)
 * 2.代理对象和目标对象是两个对象，他们并不共用数据
 *  容器中不含有目标对象，代理对象实现了advised接口，该接口的getTargetSource().getTarget()方法会返回目标对象
 *  从后初始化阶段开始，代理对象的生命周期才开始生效
 * 3.static、final、private方法无法被代理增强，因为这三种方法无法被重写
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class _1_SpringProxy {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(_1_SpringProxy.class);
        /**
         * 容器创建proxy过程中，执行了该bean的依赖注入和初始化，但并没有被增强
         * 只有成品创建出来了，再调用成品的方法，才会被增强
         */
        //proxy实际是代理对象，继承了Target类，并对Target方法进行了增强
        Target proxy = context.getBean(Target.class);
        proxy.autowired(new Bean());
        proxy.init();

        /**
         * 上面得到的proxy实际是代理对象，用下面方法得到proxy目标对象，观察代理和目标对象的成员变量
         * 发现打印结果中，代理对象的成员变量为null，因为调用代理对象的autowired，代理对象会调用目标对象的方法，因此赋值也是赋给目标对象
         * 因为，正常使用代理对象，应该走他的方法而不是成员变量
         */
        if (proxy instanceof Advised) {
            Advised advised = (Advised) proxy;
            Object target = advised.getTargetSource().getTarget();
            Target targetTarget = (Target) target;
            //bean1是代理对象，获取代理对象的bean2属性，发现为空
            System.out.println("代理对象bean2属性:" + proxy.bean);
            //bean1是目标对象Bean1，调用其属性，发现有值，说明代理对象和目标对象不共用属性
            System.out.println("目标对象bean2属性:" + targetTarget.bean);
        }
        /**
         * 因为后初始化阶段时，才生成代理对象，在这之前的依赖注入和初始化阶段，代理对象的生命周期都没有生效
         * 所以不要直接从代理对象中获取属性，但是可以调用代理对象的方法，因为代理对象的方法会再次调用目标对象的方法
         * 代理对象.属性1 ❌
         * 代理对象.方法1() ✔
         */
        //调用实例方法，发现被增强了
        proxy.test();
        //调用静态方法
        Method test1 = proxy.getClass().getMethod("test1");
        test1.invoke(proxy);
        //调用final方法
        proxy.test2();
        //调用私有方法
        //Method test3 = proxy.getClass().getDeclaredMethod("test3");
        //test3.setAccessible(true);
        //test3.invoke(proxy);
        /**
         * 而方法也不能是static，final，private方法，因为这些方法无法重写，也就无法增强
         * 代理对象不就是重写了目标类的方法，然后在重写的方法里调用目标方法，并在调用前后执行增强逻辑吗？
         * 而static，final，private方法无法做到方法重写，自然也谈不上增强
         */
        context.close();
    }
}
